/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.commons.utils;

/**
 * Base class for background processes that performs asynchronous application logic.
 * <p>See example of implementation {@link #run()} method for understand
 * <i>cancellable</i> mechanism.
 */
public abstract class BackgroundProcess implements Runnable, Cancellable {

    private final StateHandler[] stateHandlers;
    private boolean cancelThread = false;

    //----------------------------------------------------------------------

    /**
     * Initialize instance without process state handler.
     */
    protected BackgroundProcess() {
        stateHandlers = new StateHandler[]{new StateAdapter()};
    }

    /**
     * initialize instance.
     * @param stateHandler process state handler, that enables handling of
     * process state change.
     */
    protected BackgroundProcess(StateHandler... stateHandler) {
        this.stateHandlers = stateHandler;
    }

    //----------------------------------------------------------------------

    /**
     * Subclasses must return true or false according to how it supports
     * canceling process during it's works.
     * @return true if supports aborting process's works.
     */
    public abstract boolean isCancellable();

    /**
     * Subclasses must returns non-null string that means what process to do.
     * @return info string about process.
     */
    public abstract String getInfo();

    /**
     * This method runs asynchronously and performs background process logic.
     * <p>For support of process state handling, implementation must call
     * {@link #processStarted()} on begin, {@link #processFinished()} on the end,
     * {@link #processCancelled()} when cancelled (optional) and
     * {@link #processFinishedWithError(Throwable)} when fails.
     * <p>For support of cancelling process implementation must chack if
     * was cancell requested using method {@link #cancelRequest()}.
     * <p>Example:<br>
     * <pre>
     *  try {
            processStarted();
            for (int i = 0; i < 20; i++) {
                Thread.sleep(1000);
                if(cancelRequest()){        //For support of cancelling
                    processCancelled();
                    return;
                }
            }
            processFinished();
        }catch(Throwable t){
            processFinishedWithError(t);
        }
     * </pre>
     */
    public abstract void run();

    /**
     * Starts asynchronosly runing process.
     */
    public void start(){
        new Thread(this, getClass().getSimpleName()).start();
    }

    /**
     * Cancels (make request for abort) process.
     * @return true if cancel request was made successfully, false if not
     * (process doesn't supports cancelling).
     */
    public final boolean cancel(){
        if(!isCancellable()){
            return false;
        }
        cancelThread = true;
        return true;
    }

    /**
     * Returns true if a cancel was made. Use this method in {@link #run()}
     * implementation.
     * @return true if there is a request for abort running process.
     */
    protected final boolean cancelRequest(){
        return cancelThread;
    }

    public boolean isCancelled() {
        return cancelRequest();
    }

    protected synchronized final void processStarted(){
        for (StateHandler stateHandler : stateHandlers) {
            stateHandler.processStarted(getInfo());
        }
    }

    protected synchronized final void processCancelled(){
        for (StateHandler stateHandler : stateHandlers) {
            stateHandler.processCancelled(getInfo());
        }
    }

    protected synchronized final void processFinished(){
        for (StateHandler stateHandler : stateHandlers) {
            stateHandler.processFinished(getInfo());
        }
    }

    protected synchronized final void processFinishedWithError(Throwable t){
        for (StateHandler stateHandler : stateHandlers) {
            stateHandler.processFinishedWithError(getInfo(), t);
        }
    }

    @Override
    public String toString() {
        return getInfo();
    }

    //----------------------------------------------------------------------

    /**
     * Handle of process state change.
     */
    public static interface StateHandler {
        void processStarted(String processInfo);
        void processCancelled(String processInfo);
        void processFinished(String processInfo);
        void processFinishedWithError(String processInfo, Throwable t);
    }

    /**
     * Dummy implementation of {@link StateHandler}.
     */
    public static class StateAdapter implements StateHandler {
        public void processStarted(String processInfo) {
        }
        public void processCancelled(String processInfo) {
        }
        public void processFinished(String processInfo) {
        }
        public void processFinishedWithError(String processInfo, Throwable t) {
        }
    }

}
